#include "triquad.h"
#include <QVector2D>

TriQuad::TriQuad(QObject *parent) : QObject(parent)
{
    changed = true;
}

TriQuad::TriQuad(const QVector2D &v1, const QVector2D& v2, const QVector2D& v3,QObject *parent) :
    QObject(parent)
{
    vertices[0] = QVector3D(v1, 1.0);
    vertices[1] = QVector3D(v2, 1.0);
    vertices[2] = QVector3D(v3, 1.0);
    changed = true;
}

TriQuad::TriQuad(const TriQuad& tq, QObject *parent) :
    QObject(parent)
{
    *this = tq;
    changed = true;
}

QVector3D TriQuad::bariCoord(const QVector2D& p)const
{
    float areaTotal;
    QVector3D area;
    QVector3D p0 = QVector3D(vTo2DAffine(0), 0.0);
    QVector3D p1 = QVector3D(vTo2DAffine(1), 0.0);
    QVector3D p2 = QVector3D(vTo2DAffine(2), 0.0);
    QVector3D pp = QVector3D(p, 0.0);

    areaTotal = (QVector3D::crossProduct(p1-p0, p2-p0)).length();

    area.setX((QVector3D::crossProduct(p2-p1,pp-p1)).length()/areaTotal);
    area.setY((QVector3D::crossProduct(p0-p2,pp-p2)).length()/areaTotal);
    area.setZ((QVector3D::crossProduct(p1-p0,pp-p0)).length()/areaTotal);

    return area;
}

const QVector3D& TriQuad::v(int i)const
{
    if(i >= 0 && i < 3)
        return vertices[i];
    else
        return vertices[0];
}
QVector3D &TriQuad::v(int i)
{
    changed = true;
    if(i >= 0 && i < 3)
        return vertices[i];
    else
        return vertices[0];
}

QVector2D TriQuad::vTo2DAffine(int i)const
{
    if(i >= 0 && i < 3)
        return vertices[i].toVector2D()/vertices[i].z();
    else
        return vertices[0].toVector2D()/vertices[0].z();
}

const QMatrix3x3& TriQuad::q(int i)const
{
    if(i >= 0 && i < 3)
        return quads[i];
    else
        return quads[0];
}

QMatrix3x3 &TriQuad::q(int i)
{
    changed = true;
    if(i >= 0 && i < 3)
        return quads[i];
    else
        return quads[0];
}

const TriQuad& TriQuad::operator=(const TriQuad& tq)
{
    changed = true;
    for(int i = 0; i < 3; ++i)
    {
        v(i) = tq.v(i);
        q(i) = tq.q(i);
    }
    return *this;
}

QVector<QVector2D> TriQuad::getTriangleLines()const
{
    QVector<QVector2D> ret;
    ret.append(vTo2DAffine(0));
    ret.append(vTo2DAffine(1));
    ret.append(vTo2DAffine(2));
    ret.append(vTo2DAffine(0));
    return ret;
}

QVector<QVector2D> TriQuad::getCurveLines()
{
    return QVector<QVector2D>();
}

QMatrix3x3 TriQuad::makeQuadric(float x2, float y2, float xy, float x, float y, float c)
{
    QMatrix3x3 m;

    m(0,0) = x2;
    m(0,1) = m(1,0) = xy/2.0;
    m(0,2) = m(2,0) = x/2.0;
    m(1,1) = y2;
    m(1,2) = m(2,1) = y/2.0;
    m(2,2) = c;

    return m;
}

float TriQuad::eval(const QVector2D& p)const
{
    QVector3D bC = bariCoord(p);
    QVector3D t(p, 1.0 - p.x() - p.y());

    QMatrix3x3 Q = affineSum(quads[0], quads[1], quads[2], bC);

    QGenericMatrix<1, 3, qreal> P;
    P(0,0) = p.x();
    P(1,0) = p.y();
    P(2,0) = 1.0;

    return (P.transposed()*(Q*P))(0,0);
}

QVector<QVector2D> TriQuad::getPointCloud()
{
    if(changed == false)
        return points;

    QVector<QVector2D> ret;

    int numLayers = 1000;

    QVector3D V0(vertices[0]);
    QVector3D V01(vertices[1] - vertices[0]);
    QVector3D V02(vertices[2] - vertices[0]);

    float dt = 1.0/numLayers;
    float t = 1.0;
    for(int it = 0; it <= numLayers; ++it)
    {
        float smax = 1.0 - t;
        int nums = it + 1;
        float ds = smax/float(nums-1);
        float s = 0.0;
        for(int is = 0; is < nums; ++is)
        {
            QVector3D p = V0 + s*V01 + t*V02;
            float f = eval(p.toVector2D()/p.z());
            if( qAbs(f) < 0.001 )
            {
                QVector2D point = p.toVector2D()/p.z();
                ret.append(point);
            }
            s += ds;
        }
        t -= dt;
    }

    points = ret;
    changed = false;
    return ret;
}
